home *** CD-ROM | disk | FTP | other *** search
/ The World's Largest Collection of Windows Software / The World's Largest Collection of Windows Software - Disc 1.iso / connect / _j2 / wvnsc926 / headarry.c < prev    next >
C/C++ Source or Header  |  1994-09-21  |  18KB  |  694 lines

  1.  
  2. /* headarry.c
  3.  * author: Sam Rushing
  4.  * $Id: headarry.c 1.9 1994/09/18 00:45:40 jcooper Exp $
  5.  * $Log: headarry.c $
  6.  * Revision 1.9  1994/09/18  00:45:40  jcooper
  7.  * rearranged headers to allow use of precompiled headers
  8.  *
  9.  * Revision 1.8  1994/01/21  23:17:18  rushing
  10.  * Documented the thread sort algorithm.
  11.  *
  12.  * Revision 1.7  1993/12/08  01:27:21  rushing
  13.  * new version box and cr lf consistency
  14.  * *
  15.  * routines for handling the array of header information
  16.  *
  17.  * Revision 1.6  1993/06/25  20:44:45  dumoulin
  18.  * Change dates from strings to Posix standard date formats
  19.  *
  20.  * Revision 1.5  1993/06/10  18:29:34  rushing
  21.  * added set_index_to_identity to prepare for writing article ranges
  22.  *
  23.  * Revision 1.4  1993/06/08  19:41:56  rushing
  24.  * changes to support Sort... menu
  25.  *
  26.  * Revision 1.3  1993/06/05  03:18:25  rushing
  27.  * primitive functional threading.
  28.  *
  29.  * Revision 1.2  1993/06/04  16:46:44  rushing
  30.  * stable version of shellsort
  31.  *
  32.  * Revision 1.1  1993/06/01  18:15:12  rushing
  33.  * Initial revision
  34.  *
  35.  *
  36.  */ 
  37.  
  38. #include <windows.h>
  39. #include <windowsx.h>
  40. #include "wvglob.h"
  41. #include "winvn.h"
  42. #pragma hdrstop
  43.  
  44. /* The header and thread arrays are set up as follows:
  45.  * When we allocate space for the TypHeader array, we leave room for a
  46.  * pointer at the very front of that space, which will either indicate 
  47.  * that there is no thread index array (== NULL) or will point to that
  48.  * array.  Since windows can move both of these arrays around, this
  49.  * slot is updated whenever lock_headers is called.
  50.  * 
  51.  * After initialize_header_array is called, this sequence of
  52.  * operations can be used to access the header array:
  53.  * 1. header_p headers = lock_headers (header_handle, thread_handle);
  54.  * 2. header_elt (headers, index);
  55.  * 3. unlock_headers (header_handle, thread_handle);
  56.  *
  57.  * The thread_index array is an array of longs, each an index into the
  58.  * the real header array.  If thread_index is allocated and filled,
  59.  * header_elt will indirect through it.  Sorting the array of indices
  60.  * will sort the headers accordingly.
  61.  */ 
  62.  
  63. /* globals, yuck! */
  64. header_p g_headers;
  65. thread_array g_index, g_parent, g_parsort;
  66. long g_length;
  67.  
  68. /* lock the headers and the thread index array in memory for access */
  69.  
  70. header_p
  71. lock_headers (HANDLE header_handle,HANDLE thread_handle)
  72. {
  73.   thread_array_p indirect;
  74.   header_p headers;
  75.  
  76.   /* lock the header array in position */
  77.   indirect = (thread_array_p) GlobalLock (header_handle);
  78.  
  79.   headers = (header_p) ((char_p) indirect + sizeof (char_p));
  80.   
  81.   /* if we've a valid thread_handle, lock the thread_array, too */
  82.   if (thread_handle) {
  83.     *indirect = (thread_array) GlobalLock (thread_handle);
  84.   }
  85.   else
  86.     *indirect = NULL;
  87.   
  88.   return (headers);
  89. }
  90.  
  91. /* unlock the headers and thread index array */
  92.  
  93. void
  94. unlock_headers (HANDLE header_handle, HANDLE thread_handle)
  95. {
  96.   GlobalUnlock (header_handle);
  97.   if (thread_handle)
  98.     GlobalUnlock (thread_handle);
  99. }
  100.  
  101.  
  102. /* return the header memory to windows */
  103.  
  104. void
  105. free_headers (HANDLE header_handle, HANDLE thread_handle)
  106. {
  107.   GlobalFree (header_handle);
  108.   if (thread_handle)
  109.     GlobalFree (thread_handle);
  110. }
  111.  
  112.  
  113. void
  114. set_index_to_identity (HANDLE header_handle, HANDLE thread_handle, long length)
  115. {
  116.   long i;
  117.   header_p headers;
  118.   thread_array thread_index;
  119.   thread_array_p thread_index_p;
  120.  
  121.   headers = lock_headers (header_handle, thread_handle);
  122.  
  123.   if (thread_handle) {
  124.     thread_index_p  = (thread_array_p) ((char_p) headers - sizeof (char_p));
  125.     thread_index = *thread_index_p;
  126.     
  127.     /* Initialize with identity */
  128.     for (i=0; i <length ; i++)
  129.       thread_index[i] = i;
  130.   }
  131. }
  132.  
  133.  
  134. /* set up the header array, and possibly the thread index array */
  135.  
  136. void
  137. initialize_header_array (HANDLE header_handle, HANDLE thread_handle, long length)
  138. {
  139.   long i;
  140.   header_p headers;
  141.   thread_array thread_index;
  142.   thread_array_p thread_index_p;
  143.  
  144.   headers = lock_headers (header_handle, thread_handle);
  145.  
  146.   if (thread_handle) {
  147.     thread_index_p  = (thread_array_p) ((char_p) headers - sizeof (char_p));
  148.     thread_index = *thread_index_p;
  149.     
  150.     /* Initialize with identity */
  151.     for (i=0; i <length ; i++)
  152.       thread_index[i] = i;
  153.   }
  154.  
  155.   for (i=0; i < length ; i++) {
  156.     headers[i].Seen = (char) 0;
  157.     headers[i].Selected = (char) 0;
  158.     headers[i].number = 0;
  159.     headers[i].thread_depth = 0;
  160.     headers[i].lines = 0;
  161.     headers[i].date =  0;
  162.     headers[i].subject[0] = (char) 0;
  163.     headers[i].from[0] = (char) 0;
  164.     headers[i].message_id[0] = (char) 0;
  165.     headers[i].references[0] = (char) 0;
  166.     headers[i].ArtDoc = NULL;
  167.   }
  168.   unlock_headers (header_handle, thread_handle);
  169. }
  170.  
  171. /* Use this routine to get at an element of the header array - it  */
  172. /* will automatically indirect through the thread index array if it's */
  173. /* there. */
  174.  
  175. header_p
  176. header_elt (header_p headers, long index)
  177. {
  178.   
  179.   thread_array thread_index;
  180.   thread_index  = *((thread_array_p) ((char_p) headers - sizeof (char_p)));  
  181.  
  182.   if (thread_index) {
  183.     return (&(headers[thread_index[index]]));
  184.   }
  185.   else
  186.     return (&(headers[index]));
  187. }
  188.       
  189. int
  190. compare_artnum (header_p headers,
  191.         long elem1, long elem2)
  192. {
  193.   long e1,e2;
  194.   e1=headers[elem1].number;
  195.   e2=headers[elem2].number;
  196.   if (e1 == e2)
  197.     return 0;
  198.   else if (e1 < e2)
  199.     return -1;
  200.   else
  201.     return 1;
  202. }
  203.  
  204.       
  205. int
  206. compare_lines (header_p headers,
  207.         long elem1, long elem2)
  208. {
  209.   long e1,e2;
  210.   e1=headers[elem1].lines;
  211.   e2=headers[elem2].lines;
  212.   if (e1 == e2)
  213.     return 0;
  214.   else if (e1 < e2)
  215.     return -1;
  216.   else
  217.     return 1;
  218. }
  219.  
  220. int  
  221. compare_message_id (header_p headers,
  222.             long elem1, long elem2)
  223. {
  224.   return strcmp (headers[elem1].message_id , headers[elem2].message_id);
  225. }
  226.  
  227.   
  228. int
  229. compare_subject (header_p headers,
  230.          long elem1, long elem2)
  231. {
  232.   return stricmp (headers[elem1].subject , headers[elem2].subject);
  233. }
  234.  
  235. int
  236. compare_from (header_p headers,
  237.          long elem1, long elem2)
  238. {
  239.   return stricmp (headers[elem1].from , headers[elem2].from);
  240. }
  241.  
  242. int
  243. compare_date (header_p headers,
  244.           long elem1, long elem2)
  245. {
  246.   long e1,e2;
  247.   e1=headers[elem1].date;
  248.   e2=headers[elem2].date;
  249.   if (e1 == e2)
  250.     return 0;
  251.   else if (e1 < e2)
  252.     return -1;
  253.   else
  254.     return 1;
  255. }
  256.  
  257. /* this is the shell sort taken from shellsor.c and modified for */
  258. /* threading purposes... The extra comparison is to make the sort */
  259. /* stable w.r.t. article number */
  260.  
  261. void
  262. shell_sort_index_array (header_p headers,
  263.             thread_array index,
  264.             long nElements,
  265.             int (*compare) (header_p headers,
  266.                     long elem1,
  267.                     long elem2))
  268. {
  269. #define STRIDE_FACTOR 3
  270.   int c, d, stride;
  271.   int found;
  272.  
  273.   stride = 1;
  274.   while (stride <= nElements)
  275.     stride = stride * STRIDE_FACTOR + 1;
  276.  
  277.   while (stride > (STRIDE_FACTOR - 1))
  278.     {
  279.       stride = stride / STRIDE_FACTOR;
  280.       for (c = stride; c < nElements; c++)
  281.     {
  282.       found = 0;
  283.       d = c - stride;
  284.       while ((d >= 0) && !found)
  285.         {
  286.           int comp = compare (headers, index[d + stride], index[d]);
  287.           if ((comp < 0) ||
  288.           ((comp == 0) &&
  289.            (compare_artnum (headers, index[d + stride], index[d]) < 0)))
  290.         {
  291.           long tmp = index[d];
  292.           index[d] = index[d+stride];
  293.           index[d+stride] = tmp;
  294.           d -= stride;
  295.         }
  296.           else
  297.         {
  298.           found = 1;
  299.         }
  300.         }
  301.     }
  302.     }
  303. }
  304.  
  305.  
  306. void
  307. shell_sort_parent_array (thread_array index,
  308.              thread_array parents,
  309.              long n)
  310. {
  311. #define STRIDE_FACTOR 3
  312.   int c, d, stride;
  313.   int found;
  314.  
  315.   stride = 1;
  316.   while (stride <= n)
  317.     stride = stride * STRIDE_FACTOR + 1;
  318.  
  319.   while (stride > (STRIDE_FACTOR - 1))
  320.     {
  321.       stride = stride / STRIDE_FACTOR;
  322.       for (c = stride; c < n ; c++)
  323.     {
  324.       found = 0;
  325.       d = c - stride;
  326.       while ((d >= 0) && !found)
  327.         {
  328.           long p1 = parents[index[d+stride]];
  329.           long p2 = parents[index[d]];
  330.  
  331.           if ((p1 < p2) || ((p1 == p2) && (index[d+stride] > index[d])))
  332.         {
  333.           long tmp = index[d];
  334.           index[d] = index[d+stride];
  335.           index[d+stride] = tmp;
  336.           d -= stride;
  337.         }
  338.           else
  339.         {
  340.           found = 1;
  341.         }
  342.         }
  343.     }
  344.     }
  345. }
  346.  
  347. long
  348. bsearch_parsort_table (thread_array parsort,
  349.                thread_array parents,
  350.                long looking_for,
  351.                long n)
  352. {
  353.   long p;  
  354.   long high = n;
  355.   long low = 0;
  356.   
  357.   while ((high - low) > 1) {
  358.     p = (high + low) / 2;
  359.     if (looking_for <= parents[parsort[p-1]])
  360.       high = p;
  361.     else
  362.       low = p;
  363.   }
  364.   if (looking_for == parents[parsort[high-1]])
  365.     return (high-1);
  366.   else
  367.     return -1;
  368. }
  369.  
  370.  
  371. long
  372. bsearch_mid_table (header_p headers,
  373.            thread_array index, /* sorted index */
  374.            char * mid,    /* message_id we're looking for */
  375.            long n)
  376. {
  377.   long p;  
  378.   long high = n;
  379.   long low = 0;
  380.   
  381.   while ((high - low) > 1) {
  382.     p = (high + low) / 2;
  383.     if (strcmp (mid, headers[index[p-1]].message_id) <= 0)
  384.       high = p;
  385.     else
  386.       low = p;
  387.   }
  388.   if (strcmp (mid, headers[index[high-1]].message_id) == 0)
  389.     return (high-1);
  390.   else
  391.     return -1;
  392. }
  393.  
  394.  
  395. long
  396. thread_sort (long root_index, long start, long end, int depth)
  397. {
  398.   long j;
  399.   long num_children = 0;
  400.   long child_start;
  401.  
  402.   if (start == end)
  403.     return end;
  404.   else {
  405.     /* find the children of this node, pack them in the bottom */
  406.  
  407.     /* this will find the first child in the sorted table */
  408.     child_start = bsearch_parsort_table (g_parsort,
  409.                      g_parent,
  410.                      root_index,
  411.                      g_length);
  412.  
  413.     /* for each child, find its index and push on the stack in g_index */
  414.     if (child_start != -1) {
  415.       while ((child_start < g_length) &&
  416.          (g_parent[g_parsort[child_start]] == root_index)) {
  417.     g_index[end-num_children-1] = g_parsort[child_start];
  418.     child_start++;
  419.     num_children++;
  420.       }
  421.     }
  422.     /* no children found */
  423.     else
  424.       return (start);
  425.  
  426.     /* apply sort-me to each of the children */
  427.  
  428.     if (num_children == 0)
  429.       return (start);
  430.  
  431.     for (j = num_children ; j > 0 ; j--) {
  432.       g_index[start] = g_index[end-j];
  433.       g_headers[g_index[start]].thread_depth = (char) depth;
  434.       start = thread_sort (g_index[start], start+1, end-(j-1), depth+1);
  435.     }
  436.     return (start);
  437.   }
  438. }
  439.  
  440.  
  441. /* setup for threading algorithm:
  442.  * 1. allocate an extra thread_array for holding a sorted message-id table
  443.  * 2. using mid_table, allocate and create another table, a map from
  444.  *    article_index->parent_index  
  445.  * 3. sort this table by parent_index (must be a stable sort)
  446.  * 
  447.  * the recursive thread_sort will use these tables to do a stable sort
  448.  * by threads...
  449.  */
  450.  
  451. void
  452. sort_by_threads (HANDLE header_handle, HANDLE thread_handle, long length)
  453. {
  454.   long i;
  455.   header_p headers;
  456.   HANDLE mid_handle, parent_handle;
  457.   thread_array thread_index,mid_table,parent_table;
  458.   
  459.   headers = lock_headers (header_handle, thread_handle);
  460.   thread_index  = *((thread_array_p) ((char_p) headers - sizeof (char_p)));  
  461.  
  462.   if (!thread_index)
  463.     return;
  464.  
  465.   mid_handle = GlobalAlloc (GMEM_MOVEABLE, (long) (sizeof(long) * length));
  466.   if (!mid_handle)
  467.     return;
  468.  
  469.   parent_handle = GlobalAlloc (GMEM_MOVEABLE, (long) (sizeof (long) * length));
  470.   if (!parent_handle) {
  471.     GlobalFree (mid_handle);
  472.     return;
  473.   }
  474.     
  475.   mid_table = (thread_array) GlobalLock (mid_handle);
  476.   parent_table = (thread_array) GlobalLock (parent_handle);
  477.   
  478.   /* create the message_id table */
  479.   for (i=0 ; i < length; i++) mid_table[i]=i;
  480.   shell_sort_index_array (headers, mid_table, length, compare_message_id);
  481.  
  482.   /* create the parent table */
  483.   
  484.   for (i=0 ; i < length; i++) {
  485.     long p = bsearch_mid_table (headers,mid_table,headers[i].references,length);
  486.     parent_table[i] = (p == -1) ? -1 : mid_table[p];
  487.   }
  488.  
  489.   /* clear it so we can debug */
  490.   for (i=0 ; i< length; i++)
  491.     thread_index[i]=0;
  492.  
  493.   /* re-use the mid_table as a sorted parent table */
  494.   for (i=0 ; i< length; i++)
  495.     mid_table[i]=i;
  496.  
  497.   shell_sort_parent_array (mid_table, parent_table, length);
  498.  
  499.   g_headers = headers;
  500.   g_index = thread_index;
  501.   g_parent = parent_table;
  502.   g_parsort = mid_table;
  503.   g_length = length;
  504.  
  505.   thread_sort (-1, 0, length,0);
  506.  
  507.   GlobalFree (parent_handle);
  508.   GlobalFree (mid_handle);
  509. }
  510.  
  511. /*
  512. --------------------------------------------------------------------------
  513.  
  514. Thread sorting algorithm.
  515.  
  516. Here's an example, already threaded, showing the relationship between
  517. parent, child, and original index.
  518.  
  519.  
  520.  
  521. 0   5
  522. 1      2
  523. 2         3
  524. 3      0   
  525. 4   1   
  526. 5      4        
  527.  
  528. On the display, it may look like this:
  529.  
  530. 5   Why my computer is better than yours...
  531. 2     Re: Why my computer is better than yours...
  532. 3       Why my _car_ is better than your computer (was Re: ...)
  533. 0     Re: Why my computer is better than yous...
  534. 1   What's the latest on the Amiga 6000???
  535. 4     What planet are you on ? (was Re: What's the latest...)
  536.  
  537. This shows that articles #2 and #0 are in response to article #5 (yes this can
  538. and does happen), article #4 is in response to #1, etc...
  539.  
  540. This is the parent table.  It answers the question: "what is the index
  541. of my parent".  '*' means root, or 'no' index - either there is no    
  542. parent of this article, or we don't have it.  In the code, '*' == -1.
  543.  
  544.   |--|
  545. 0 |5 |
  546.   |--|
  547. 1 |* | 
  548.   |--|
  549. 2 |5 |
  550.   |--|
  551. 3 |2 |
  552.   |--|
  553. 4 |1 |
  554.   |--|
  555. 5 |* |
  556.   |--|
  557.  
  558. This table was computed by
  559. 1) sorting by Message-ID (by creating 'mid_table') with a shell sort.
  560. 2) use 'mid_table' to find each article's parent (using a binary search),
  561.    thereby creating 'parent_table'.
  562. 3) mid_table's not needed any longer, so we now re-use it as a sorted
  563.    index into parent_table.  More on this later.
  564.  
  565. What we want from thread_sort is for the empty thread_index to hold
  566. an array with these articles indices in the correct order:
  567.                                          
  568. |--|
  569. |5 |
  570. |--|
  571. |2 |
  572. |--|
  573. |3 |
  574. |--|
  575. |0 |
  576. |--|
  577. |1 |
  578. |--|
  579. |4 |
  580. |--|
  581.  
  582. ---------------------------------------------------------------------------
  583. Now, for the actual algorithm.  The work is performed by the recursive
  584. function thread_sort().
  585.  
  586. thread_sort (root_index, start, end, depth) { ... }
  587. (depth is used to keep track of the depth of the recursion)
  588.  
  589. 1) Start with an empty index table.  start = 0 (the beginning of the
  590.    table), and end = length (the length of the table).
  591.  
  592. 2) Find all the children of the current root, (which is '*' to start
  593.    with), and pack them (in order) into the bottom of the table.
  594.    If there are no children, return 'start'.
  595.    If start == end, return 'start'.
  596.  
  597. 3) Now, we will recurse [call thread_root()] for each of these
  598.    children, using the empty portion of thread_index to work with.
  599.    We do this by:
  600.      
  601.    3a) Move child #1 into the top slot.
  602.    3b) calling thread_sort, with
  603.        root_index = child #1
  604.        start = start of the empty portion
  605.        end = end of the empty portion
  606.        depth = depth+1
  607.    3c) After thread_sort() does its magic, it will return a 
  608.        new value for 'start', indicating where the 'work area'
  609.        can start.  thread_sort() may have filled in an arbitrary
  610.        number of slots in this call, but will never overstep the
  611.        free space.  Don't worry, it all works out.  8^)
  612.  
  613.    3d) Go to child #2, repeat 3(a-c), #3, #4, etc...
  614.  
  615. 4) return 'start' (the start of the empty space).
  616.                   
  617. Here's a trace of the algorithm using our example articles.
  618. ---------------------------------------------------------------------------
  619. The number above the stack of boxes indicates the parent that
  620. we're finding the children of.
  621.  
  622.    *                      
  623.   |--|  |--|                                                
  624. 0 |  |  |5 |   5                                            
  625.   |--|  |--|  |--|  |--|                          |--|  |--|
  626. 1 |  |  |  |  |  |  |2 |   2                      |2 |  |2 |
  627.   |--|  |--|  |--|  |--|  |--|  |--|        |--|  |--|  |--|
  628. 2 |  |  |  |  |  |  |  |  |  |  |3 |   3    |3 |  |3 |  |3 |    ==>
  629.   |--|  |--|  |--|  |--|  |--|  |--|  |--|  |--|  |--|  |--|
  630. 3 |  |  |  |  |2 |  |  |  |3 |  |  |  |  |  |  |  |  |  |  |
  631.   |--|  |--|  |--|  |--|  |--|  |--|  |--|  |--|  |--|  |--|
  632. 4 |5 |  |  |  |0 |  |0 |                                |0 |
  633.   |--|  |--|  |--|  |--|                                |--|
  634. 5 |1 |  |1 |                                                
  635.   |--|  |--|                                                
  636.                                                           
  637.  
  638. continued...
  639.                                          
  640.                     |--|                    |--|
  641. 0                   |5 |                    |5 |
  642.               |--|  |--|                    |--|
  643. 1             |2 |  |2 |                    |2 |
  644.               |--|  |--|                    |--|
  645. 2             |3 |  |3 |                    |3 |
  646.   |--|        |--|  |--|                    |--|
  647. 3 |0 |   0    |0 |  |0 |                    |0 |
  648.   |--|  |--|  |--|  |--|  |--|              |--|
  649. 4 |  |  |  |  |  |  |  |  |1 |   1          |1 |
  650.   |--|  |--|  |--|  |--|  |--|  |--|        |--|
  651. 5                   |1 |  |  |  |4 |   4    |4 |
  652.                     |--|  |--|  |--|  |--|  |--|
  653.  
  654.  
  655. ---------------------------------------------------------------------------
  656. Now that you understand the algorithm 8^), we go back to parsort_table.  
  657. At the start of each call to thread_sort(), we need to find all the
  658. children of root_index.  We can do this quickly by using a sorted
  659. version of parent_table, called parsort_table.  It contains a
  660. convenient ordered list of children for us:
  661.  
  662.   x -+
  663.   x  | all the children of 'x' 
  664.   x  |                   
  665.   x -+                        
  666.   y -+            
  667.   y -+ all the children of 'y'
  668.   z -+
  669.   z  |
  670.   z  | all the children of 'z' 
  671.   z  | 
  672.   z -+
  673.  
  674. A single call (order logn) to bsearch_parsort_table puts us at the
  675. correct index for finding all the children we are looking for.
  676.  
  677. parsort_table
  678.   |--|
  679. 0 |1 |
  680.   |--|
  681. 1 |5 | 
  682.   |--|
  683. 2 |4 |
  684.   |--|
  685. 3 |3 |
  686.   |--|
  687. 4 |0 |
  688.   |--|
  689. 5 |2 |
  690.   |--|
  691.  
  692. ---------------------------------------------------------------------------
  693.  
  694. */